/**
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 */
package org.jiopi.ibean.kernel.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import org.apache.log4j.Logger;
import org.jiopi.ibean.kernel.config.ConfigConstants;
import org.jiopi.ibean.share.ShareUtil.IOUtil;
import org.jiopi.framework.annotation.blueprint.*;
import org.jiopi.framework.annotation.module.*;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

/**
 * 
 * JIOPi标注解析器
 * <P>
 * 用于从给定的Jar文件中实时解析出与Annotation一致的xml文件，并写入给定的输出流
 * 如果不是标准JIOPi 模块Jar 或 蓝图Jar 则不写入任何内容
 * </P>
 * @since 2010.5.11
 *
 */
public class AnnotationParser {
	
	private static Logger logger = Logger.getLogger(AnnotationParser.class);
	
	public static String[] configFileNames = new String[]{"blueprint.xml","module.xml"};
	
	private static int parseClassPath(File classPath,OutputStream out,boolean parseAnnotation,boolean getLocalFile){
		if(!classPath.isDirectory()){
			return -1;
		}
		ClassLoader cl = null;
		try {
			File configEntry = null;
			int testJarType = -1;
			for(String configFileName:configFileNames){
				testJarType++;
				configEntry = new File(classPath,configFileName);
				if(configEntry.isFile()) break;
			}
			if(configEntry!=null&&configEntry.isFile()){
				if(getLocalFile){
					InputStream in = new FileInputStream(configEntry);
					try{
						IOUtil.copyStreams(in, out);
					}finally{
						IOUtil.close(in);
					}
				}
				return testJarType;
			}else{
				int jarType = -1;
				for(String configFileName:configFileNames){
					jarType++;
					configEntry = new File(classPath,configFileName+".jiopi");
					if(configEntry.isFile()) break;
				}
				if(configEntry==null||!configEntry.isFile()){
					return -1;
				}
				if(!parseAnnotation)return jarType;
				//System.out.println("===parseJarFile "+jarFile);
				AnnotationParser ap = new AnnotationParser(jarType);
				cl = Thread.currentThread().getContextClassLoader();
				URLClassLoader pcl = new URLClassLoader(new URL[]{FileUtil.toURL(classPath.getAbsolutePath())},cl);
				Thread.currentThread().setContextClassLoader(pcl);
				
				ClassPool classPool = ClassPool.getDefault();
				
				parseClassPathDir(classPath,classPool,ap);
				
				Document document = null;
				if(ap.blueprint!=null)document=ap.blueprint.getDocument();
				else if(ap.module!=null)document=ap.module.getDocument();
				
				OutputFormat format = OutputFormat.createPrettyPrint();
				XMLWriter output = new XMLWriter(out,format);
				output.write(document);
				output.close();
				//System.out.println("===parseJarFile "+jarFile+" over======");
				return ap.jarType;
			}
		} catch (Exception e) {
			logger.warn("",e);
		}finally{
			if(cl!=null) Thread.currentThread().setContextClassLoader(cl);
		}
		return -1;
	}
	private static void parseClassPathDir(File dir,ClassPool classPool,AnnotationParser ap){
		if(dir.isDirectory()){
			File[] subFiles = dir.listFiles();
			for(File subFile:subFiles){
				parseClassPathDir(subFile,classPool,ap);
			}
		}else if(dir.isFile()){
			if(dir.getName().endsWith(".class")){
				InputStream in = null;
				try{
					in = new FileInputStream(dir);
					CtClass ctClass = classPool.makeClass(in);
					ap.parseClass(ctClass);
				}catch(Exception e){
					IOUtil.close(in);
				}
			}
		}
	}
		
	
	/**
	 * parse the given jar file,
	 * 
	 * write to the OutputStream when need to parse
	 * 
	 * @param jarFile
	 * @param out
	 * @param parseAnnotation only get jar file type if is false
	 * @param getLocalFile write exist xml file to the OutputStream if is true
	 * @return jar type,AnnotationParser.BLUEPRINT or AnnotationParser.MODULE,or other if not the expecting jar
	 */
	public static int parseJarFile(File jarFile,OutputStream out,boolean parseAnnotation,boolean getLocalFile){
		//jar包遍历
		if(jarFile.isDirectory()){
			return parseClassPath(jarFile,out,parseAnnotation,getLocalFile);
		}
		if(!jarFile.isFile()|| ! jarFile.getName().endsWith(".jar")){
			return -1;
		}
		JarFile jar = null;
		ClassLoader cl = null;
		try {
			jar = new JarFile(jarFile);
			JarEntry configEntry = null;
			int testJarType = -1;
			for(String configFileName:configFileNames){
				testJarType++;
				configEntry = jar.getJarEntry(configFileName);
				if(configEntry!=null) break;
			}
			if(configEntry != null){
				if(getLocalFile){
					InputStream in = jar.getInputStream(configEntry);
					IOUtil.copyStreams(in, out);
					IOUtil.close(in);
				}
				return testJarType;
			}else{
				int jarType = -1;
				for(String configFileName:configFileNames){
					jarType++;
					configEntry = jar.getJarEntry(configFileName+".jiopi");
					if(configEntry!=null) break;
				}
				if(configEntry == null){
					return -1;
				}
				if(!parseAnnotation)return jarType;
				//System.out.println("===parseJarFile "+jarFile);
				AnnotationParser ap = new AnnotationParser(jarType);
				
				cl = Thread.currentThread().getContextClassLoader();
				URLClassLoader pcl = new URLClassLoader(new URL[]{jarFile.toURI().toURL()},cl);
				
				Thread.currentThread().setContextClassLoader(pcl);
				
				ClassPool classPool = ClassPool.getDefault();
				Enumeration<JarEntry> entries = jar.entries();
				while(entries.hasMoreElements()){
					JarEntry entry = entries.nextElement();
					if(entry.getName().endsWith(".class")){//只解析class文件
						InputStream in = jar.getInputStream(entry);
						CtClass ctClass = classPool.makeClass(in);
						ap.parseClass(ctClass);
						IOUtil.close(in);
					}
				}
				
				Document document = null;
				if(ap.blueprint!=null)document=ap.blueprint.getDocument();
				else if(ap.module!=null)document=ap.module.getDocument();
				
				OutputFormat format = OutputFormat.createPrettyPrint();
				XMLWriter output = new XMLWriter(out,format);
				output.write(document);
				output.close();
				//System.out.println("===parseJarFile "+jarFile+" over======");
				return ap.jarType;
			}
		} catch (Exception e) {
			logger.warn("",e);
		}finally{
			close(jar);
			if(cl!=null) Thread.currentThread().setContextClassLoader(cl);
		}
		return -1;
	}
	
	private static void close( JarFile c ) {
		if( c!=null ) {
			try {
				c.close();
			} catch (Exception e) {
				logger.warn(e);
			}
		}
	}
	
	
	private final int jarType;
	private AnnotationParser(int jarType){
		switch(jarType){
			case ConfigConstants.BLUEPRINT_JAR:
				this.blueprint = new BlueprintConfig();
				break;
			case ConfigConstants.MODULE_JAR:
				this.module = new ModuleConfig();
				break;
		}
		this.jarType = jarType;
	}
	
	private Annotation[] preprocessCtAnnotation(Object[] annotationObjs){
		Annotation[] annotations = null;
		ArrayList<Annotation> annotationList = new ArrayList<Annotation>();
		for(Object annotationObj:annotationObjs){
			if(annotationObj instanceof Annotation){
				annotationList.add((Annotation)annotationObj);
			}
		}
		if(annotationList.size()>0){
			annotations = new Annotation[annotationList.size()];
			annotations = annotationList.toArray(annotations);
		}
		return annotations;
	}
	
	/**
	 * 分析给定的字节码解析类
	 * @param ctClass
	 */
	private void parseClass(CtClass ctClass){
		//System.out.println("parse class "+ctClass.getName());
		//ElementType.TYPE
		{
			Object[] annotationObjs = ctClass.getAvailableAnnotations();
			Annotation[] annotations = preprocessCtAnnotation(annotationObjs);
			if(annotations!=null){
				parseAnnotations(ctClass,annotations);
			}
		}
		
		
		//ElementType.FIELD
		CtField[] ctFields = ctClass.getDeclaredFields();
		for(CtField ctField:ctFields){
			Object[] annotationObjs = ctField.getAvailableAnnotations();
			Annotation[] annotations = preprocessCtAnnotation(annotationObjs);
			if(annotations!=null){
				parseAnnotations(new WrappedClass(ctClass,ctField),annotations);
			}
		}
		
		//ElementType.METHOD
		CtMethod[] ctMethods = ctClass.getDeclaredMethods();
		for(CtMethod ctMethod:ctMethods){
			if(!Modifier.isPublic(ctMethod.getModifiers()))continue;
			{
				Object[] annotationObjs = ctMethod.getAvailableAnnotations();
				Annotation[] annotations = preprocessCtAnnotation(annotationObjs);
				if(annotations!=null){
					parseAnnotations(new WrappedClass(ctClass,ctMethod),annotations);
				}
			}
			
			//ElementType.PARAMETER
			try{
				CtClass[] ctClassParameters = ctMethod.getParameterTypes();
				Object[][] parameters = ctMethod.getAvailableParameterAnnotations();
				int index = 0;
				for(Object[] annotationObjs : parameters){
					Annotation[] annotations = preprocessCtAnnotation(annotationObjs);
					if(annotations!=null){
						Object[] parameterAnnotationInfo = new Object[]{ctMethod,ctClassParameters,index};
						parseAnnotations(new WrappedClass(ctClass,parameterAnnotationInfo),annotations);
					}
					index++;
				}
			}catch(NotFoundException e){//不存在参数
				
			}
		}
		if(blueprint!=null)blueprint.addClass(ctClass);
		else if(module!=null)module.addClass(ctClass);
	}
	
	private void parseAnnotations(Object host,Annotation[] annotations){
		for(Annotation annotation :annotations){
			Class<?> annotationType = annotation.annotationType();
			if(jiopiAnnotations.contains(annotationType)){
				String parseMethodName = annotationType.getSimpleName()+"Annotation";
				try {
					Method parseMethod = AnnotationParser.class.getDeclaredMethod(parseMethodName, new Class<?>[]{Object.class,Annotation.class});
					parseMethod.invoke(this, host,annotation);
				} catch (Exception e) {
					logger.error("unexpected error when parse : "+parseMethodName,e);
				} 
			}
		}
	}
	
	private static HashSet<Class<?>> jiopiAnnotations = new HashSet<Class<?>>();
	static{
		//blueprint
		jiopiAnnotations.add(Blueprint.class);
		jiopiAnnotations.add(RegisterControlPanel.class);
		jiopiAnnotations.add(Socket.class);
		jiopiAnnotations.add(RegisterConstructor.class);
		//module
		jiopiAnnotations.add(Instantiation.class);
		jiopiAnnotations.add(RegisterModule.class);
		jiopiAnnotations.add(SocketControlPanel.class);
		jiopiAnnotations.add(SocketModule.class);
		jiopiAnnotations.add(Hotswap.class);
		jiopiAnnotations.add(Reference.class);
	}
	
	private BlueprintConfig blueprint = null;
	private ModuleConfig module = null;
	
	@SuppressWarnings("unused")
	private void BlueprintAnnotation(Object host,Annotation annotation){
		Blueprint blueprintAnnotation = (Blueprint)annotation;
		if(blueprint == null)return;
		if(host instanceof CtClass){
			CtClass ctClass = (CtClass)host;
			blueprint.name = blueprintAnnotation.name();
			blueprint.version = blueprintAnnotation.version();
			blueprint.versionInterface = blueprintAnnotation.versionInterface().getName();
		}
	}
	
	/**
	 * parse the annotation of RegisterConstructor
	 * 
	 * @param host
	 * @param annotation
	 */
	@SuppressWarnings("unused")
	private void RegisterConstructorAnnotation(Object host,Annotation annotation){
		RegisterConstructor registerConstructorAnnotation = (RegisterConstructor)annotation;
		if(blueprint == null)return;
		String constructControlpanel = registerConstructorAnnotation.value();
		if(host instanceof CtClass){
			CtClass ctClass = (CtClass)host;
			String constructorClassName = ctClass.getName();
			blueprint.constructors.put(constructorClassName, constructControlpanel);
		}
	}
	
	@SuppressWarnings("unused")
	private void RegisterControlPanelAnnotation(Object host,Annotation annotation){
		RegisterControlPanel registerControlPanelAnnotation = (RegisterControlPanel)annotation;
		if(blueprint == null)return;
		String registerName = registerControlPanelAnnotation.value();
		if(host instanceof CtClass){
			CtClass ctClass = (CtClass)host;
			BlueprintConfig.Interface setInterface = blueprint.getInterface(ctClass.getName());
			if(registerName.length()==0)registerName = ctClass.getSimpleName();
			setInterface.registerControlpanel = registerName;
		}else if(host instanceof WrappedClass){
			WrappedClass wrappedClass = (WrappedClass)host;
			Object innerMember = wrappedClass.innerMember;
			BlueprintConfig.Interface setInterface = blueprint.getInterface(wrappedClass.innerClass.getName());
			if(innerMember instanceof CtField){
				CtField ctField = (CtField)innerMember;
				if(registerName.length()==0)registerName = ctField.getName();
				setInterface.registerVariable.put(ctField.getName(), registerName);
			}else if(innerMember instanceof CtMethod){
				CtMethod ctMethod = (CtMethod)innerMember;
				if(registerName.length()==0)registerName = ctMethod.getName();
				setInterface.registerMethod.put(ctMethod.getName(), registerName);
			}
		}

	}
	@SuppressWarnings("unused")
	private void SocketAnnotation(Object host,Annotation annotation){
		Socket socketAnnotation = (Socket)annotation;
		if(blueprint == null)return;
		if(host instanceof WrappedClass){
			WrappedClass wrappedClass = (WrappedClass)host;
			Object innerMember = wrappedClass.innerMember;
			BlueprintConfig.Interface setInterface = blueprint.getInterface(wrappedClass.innerClass.getName());
			if(innerMember instanceof CtMethod){
				CtMethod ctMethod = (CtMethod)innerMember;
				setInterface.sockets.add(ctMethod.getName());
			}
		}
	}
	@SuppressWarnings("unused")
	private void HotswapAnnotation(Object host,Annotation annotation){
		Hotswap hotswapAnnotation = (Hotswap)annotation;
		if(module == null)return;
		if(host instanceof CtClass){
			CtClass ctClass = (CtClass)host;
			String resetMethod = hotswapAnnotation.resetMethod();
			PackageMerger pm = module.getHotswapPackageMerger(resetMethod);
			pm.addClass(ctClass.getName(), true,true);
		}
	}
	@SuppressWarnings("unused")
	private void InstantiationAnnotation(Object host,Annotation annotation){
		Instantiation instantiationAnnotation = (Instantiation)annotation;
		if(module == null)return;
		if(host instanceof CtClass){
			CtClass ctClass = (CtClass)host;
			ModuleConfig.ControlPanel cp = module.getControlPanel(ctClass.getName());
			InstanceType instanceType = instantiationAnnotation.type();
			String factoryMethod = instantiationAnnotation.factoryMethod();
			String initMethod = instantiationAnnotation.initMethod();
			cp.instantiation.put("instancetype", instanceType.name());
			if(factoryMethod.length()>0)cp.instantiation.put("factory-method",factoryMethod);
			if(initMethod.length()>0)cp.instantiation.put("init-method",initMethod);
		}
	}
	@SuppressWarnings("unused")
	private void RegisterModuleAnnotation(Object host,Annotation annotation){
		RegisterModule registerModuleAnnotation = (RegisterModule)annotation;
		if(module == null)return;
		if(host instanceof CtClass){
			CtClass ctClass = (CtClass)host;
			ModuleConfig.ControlPanel cp = module.getControlPanel(ctClass.getName());
			cp.registerControlpanel = true;
			String[] registerControlpanels = registerModuleAnnotation.value();
			for(String registerControlpanel:registerControlpanels){
				cp.registerControlpanels.add(registerControlpanel);
			}
		}
	}
	@SuppressWarnings("unused")
	private void SocketControlPanelAnnotation(Object host,Annotation annotation){
		SocketControlPanel SocketControlPanelAnnotation = (SocketControlPanel)annotation;
		if(module == null)return;
		if(host instanceof WrappedClass){
			WrappedClass wrappedClass = (WrappedClass)host;
			CtField ctField = (CtField)wrappedClass.innerMember;
			ModuleConfig.ControlPanel cp = module.getControlPanel(wrappedClass.innerClass.getName());
			String id = SocketControlPanelAnnotation.id();
			String module = SocketControlPanelAnnotation.module();
			String version = SocketControlPanelAnnotation.version();
			String controlpanel = SocketControlPanelAnnotation.controlpanel();
			String configuration = SocketControlPanelAnnotation.configuration();
			HashMap<String,String> socketData = cp.getSocket(ctField.getName(), cp.socketControlpanel);
			if(id.length()>0)socketData.put("id", id);
			if(module.length()>0)socketData.put("module", module);
			if(version.length()>0)socketData.put("version", version);
			if(controlpanel.length()>0)socketData.put("controlpanel", controlpanel);
			if(configuration.length()>0)socketData.put("configuration", configuration);
			
		}
	}
	@SuppressWarnings("unused")
	private void SocketModuleAnnotation(Object host,Annotation annotation){
		SocketModule socketModuleAnnotation = (SocketModule)annotation;
		if(module == null)return;
		if(host instanceof WrappedClass){
			WrappedClass wrappedClass = (WrappedClass)host;
			CtField ctField = (CtField)wrappedClass.innerMember;
			ModuleConfig.ControlPanel cp = module.getControlPanel(wrappedClass.innerClass.getName());
			String id = socketModuleAnnotation.id();
			String module = socketModuleAnnotation.module();
			String version = socketModuleAnnotation.version();
			String configuration = socketModuleAnnotation.configuration();
			HashMap<String,String> socketData = cp.getSocket(ctField.getName(), cp.socketModule);
			if(id.length()>0)socketData.put("id", id);
			if(module.length()>0)socketData.put("module", module);
			if(version.length()>0)socketData.put("version", version);
			if(configuration.length()>0)socketData.put("configuration", configuration);
			
		}
	}
	@SuppressWarnings("unused")
	private void ReferenceAnnotation(Object host,Annotation annotation){
		Reference referenceAnnotation = (Reference)annotation;
		if(module == null)return;
		if(host instanceof WrappedClass){
			WrappedClass wrappedClass = (WrappedClass)host;
			Object[] parameterAnnotationInfo = (Object[])wrappedClass.innerMember;
			CtMethod ctMethod = (CtMethod)parameterAnnotationInfo[0];
			CtClass[] parametersClass = (CtClass[])parameterAnnotationInfo[1];
			int index = (Integer)parameterAnnotationInfo[2];
			ModuleConfig.ControlPanel cp = module.getControlPanel(wrappedClass.innerClass.getName());
			ArrayList<HashMap<String,String>> methodData = cp.getMethodConfig(ctMethod.getName(), parametersClass);
			methodData.get(index).put("reference", "true");
		}
	}
	
	
	/**
	 *
	 * 蓝图标注保存类
	 *
	 */
	private static class BlueprintConfig{
		public String name = "";
		public String version = "";
		public String versionInterface = "";
		public PackageMerger exports = new PackageMerger();
		public HashMap<String,Interface> interfaces = new HashMap<String,Interface>();
		/**
		 * store the constructor configurations
		 */
		public HashMap<String,String> constructors = new HashMap<String,String>();
		
		public void addClass(CtClass blueprintClass){
			if(!blueprintClass.isAnnotation()){
				exports.addClass(blueprintClass.getName(), true,true);
			}
		}
		
		public static class Interface{
			public String registerControlpanel = "";
			public HashMap<String,String> registerMethod = new HashMap<String,String>();
			public HashMap<String,String> registerVariable = new HashMap<String,String>();
			public HashSet<String> sockets = new HashSet<String>();
		}
		
		public Interface getInterface(String name){
			Interface getInterface = interfaces.get(name);
			if(getInterface == null){
				getInterface = new Interface();
				interfaces.put(name, getInterface);
			}
			return getInterface;
		}
		public Document getDocument(){
			Document document =  DocumentHelper.createDocument();
			Element blueprintElement = document.addElement("blueprint");
			blueprintElement.addAttribute("name", this.name);
			blueprintElement.addAttribute("version", this.version);
			blueprintElement.addAttribute("xmlns", "http://www.jiopi.org/2010/JIOPISchema");
			blueprintElement.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
			blueprintElement.addAttribute("xsi:schemaLocation", "http://www.jiopi.org/2010/JIOPISchema xsd/jiopi-config-blueprint.xsd");
			
			Element versionInterfaceElement = blueprintElement.addElement("version-interface");
			versionInterfaceElement.setText(this.versionInterface);
			
			String[] exportPackages = this.exports.getExportPackages();
			if(exportPackages.length>0){
				Element exportsElement = blueprintElement.addElement("exports");
				for(String exportPackage : exportPackages){
					Element exportElement = exportsElement.addElement("export");
					exportElement.setText(exportPackage);
				}
			}
			
			for(String interfaceName:this.interfaces.keySet()){
				Interface interfaceInfo = interfaces.get(interfaceName);
				Element interfaceElement = blueprintElement.addElement("interface");
				interfaceElement.addAttribute("class", interfaceName);
				
				Element registerControlpanelElement = interfaceElement.addElement("register-controlpanel");
				registerControlpanelElement.addText(interfaceInfo.registerControlpanel);
				
				for(String variable:interfaceInfo.registerVariable.keySet()){
					Element registerVariableElement = interfaceElement.addElement("register-variable");
					registerVariableElement.addAttribute("variable", variable);
					registerVariableElement.addText(interfaceInfo.registerVariable.get(variable));
				}
				
				for(String method:interfaceInfo.registerMethod.keySet()){
					Element registerMethodElement = interfaceElement.addElement("register-method");
					registerMethodElement.addAttribute("method", method);
					registerMethodElement.addText(interfaceInfo.registerMethod.get(method));
				}
				
				for(String socket:interfaceInfo.sockets){
					Element socketElement = interfaceElement.addElement("socket");
					socketElement.addText(socket);
				}
			}
			for(String className:this.constructors.keySet()){
				String constructControlpanel = this.constructors.get(className);
				Element constructorElement = blueprintElement.addElement("constructor");
				constructorElement.addAttribute("class", className);
				Element constructControlpanelElement = constructorElement.addElement("construct-controlpanel");
				constructControlpanelElement.addText(constructControlpanel);
			}
			return document;
		}
		
	}
	
	/**
	 * 模块标注保存类
	 * @author User
	 *
	 */
	private static class ModuleConfig{
		
		
		public HashMap<String,PackageMerger> hotswaps = new HashMap<String,PackageMerger>();
		public HashMap<String,ControlPanel> controlPanels = new HashMap<String,ControlPanel>();
		private HashSet<String> classes = new HashSet<String>();
		
		public Document getDocument(){
			Document document =  DocumentHelper.createDocument();
			Element moduleElement = document.addElement("module");
			moduleElement.addAttribute("xmlns", "http://www.jiopi.org/2010/JIOPISchema");
			moduleElement.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
			moduleElement.addAttribute("xsi:schemaLocation", "http://www.jiopi.org/2010/JIOPISchema xsd/jiopi-config-module.xsd");
			
			if(hotswaps.size()>0){
				Element hotswapElement = moduleElement.addElement("hotswap");
				for(String resetName:hotswaps.keySet()){
					PackageMerger resetPackageMerger = hotswaps.get(resetName);
					for(String moduleClass:this.classes){
						resetPackageMerger.addClass(moduleClass, false, false);
					}
					String[] classes = resetPackageMerger.getExportPackages();
					for(String exportClass:classes){
						Element classElement = hotswapElement.addElement("class");
						if(!"".equals(resetName)){
							classElement.addAttribute("reset-method", resetName);
						}
						classElement.addText(exportClass);
					}
				}
			}
			for(String controlPanelClass:controlPanels.keySet()){
				ControlPanel controlPanel = controlPanels.get(controlPanelClass);
				Element controlpanelElement = moduleElement.addElement("controlpanel");
				controlpanelElement.addAttribute("class", controlPanelClass);
				if(controlPanel.registerControlpanel){
					controlpanelElement.addAttribute("register-module", "true");
				}
				
				if(controlPanel.registerControlpanels.size()>0){
					Element registerControlpanelsElement = controlpanelElement.addElement("register-controlpanels");
					for(String className:controlPanel.registerControlpanels){
						Element classElement = registerControlpanelsElement.addElement("class");
						classElement.addText(className);
					}
				}
				
				if(controlPanel.instantiation.size()>0){
					Element instantiationElement = controlpanelElement.addElement("instantiation");
					for(String key:controlPanel.instantiation.keySet()){
						Element keyElement = instantiationElement.addElement(key);
						keyElement.setText(controlPanel.instantiation.get(key));
					}
				}
				
				for(String socketModule:controlPanel.socketModule.keySet()){
					HashMap<String,String> socketModuleData = controlPanel.socketModule.get(socketModule);
					Element socketModuleElement = controlpanelElement.addElement("socket-module");
					for(String key:socketModuleData.keySet()){
						socketModuleElement.addAttribute(key, socketModuleData.get(key));
					}
					socketModuleElement.setText(socketModule);
				}
				
				for(String socketControlpanel:controlPanel.socketControlpanel.keySet()){
					HashMap<String,String> socketControlpanelData = controlPanel.socketControlpanel.get(socketControlpanel);
					Element socketControlpanelElement = controlpanelElement.addElement("socket-controlpanel");
					for(String key:socketControlpanelData.keySet()){
						socketControlpanelElement.addAttribute(key, socketControlpanelData.get(key));
					}
					socketControlpanelElement.setText(socketControlpanel);
				}
				
				for(String method:controlPanel.methods.keySet()){
					String[] methodData = method.split(",");
					ArrayList<HashMap<String,String>> parameterAnnotation = controlPanel.methods.get(method);
					if(methodData.length>1){
						Element methodElement = controlpanelElement.addElement("method");
						methodElement.addAttribute("name", methodData[0]);
						
						Element parametersElement = methodElement.addElement("parameters");
						for(int i=1;i<methodData.length;i++){
							Element parameterElement = parametersElement.addElement("parameter");
							parameterElement.addAttribute("class", methodData[i]);
							HashMap<String,String> parameterAnno = parameterAnnotation.get(i-1);
							for(String key:parameterAnno.keySet()){
								parameterElement.addAttribute(key, parameterAnno.get(key));
							}
						}
					}
					
				}
			}
			
			return document;
		}
		
		public void addClass(CtClass moduleClass){
			if(!moduleClass.isAnnotation()){
				classes.add(moduleClass.getName());
			}
		}
		
		public static class ControlPanel{
			public boolean registerControlpanel = false;
			public HashMap<String,String> instantiation = new HashMap<String,String>();
			public HashSet<String> registerControlpanels = new HashSet<String>();
			public HashMap<String,HashMap<String,String>> socketModule = new HashMap<String,HashMap<String,String>>();
			public HashMap<String,HashMap<String,String>> socketControlpanel = new HashMap<String,HashMap<String,String>>();
			public HashMap<String,ArrayList<HashMap<String,String>>> methods = new HashMap<String,ArrayList<HashMap<String,String>>>();
			
			public HashMap<String,String> getSocket(String socketName,HashMap<String,HashMap<String,String>> socket){
				HashMap<String,String> socketData = socket.get(socketName);
				if(socketData == null){
					socketData = new HashMap<String,String>();
					socket.put(socketName, socketData);
				}
				return socketData;
			}
			
			public ArrayList<HashMap<String,String>> getMethodConfig(String name,CtClass[] types){
				if(types == null || types.length<1) return null;
				StringBuilder methodNameBuilder = new StringBuilder(name);
				for(CtClass type:types){
					methodNameBuilder.append(",").append(type.getName());
				}
				String methodName = methodNameBuilder.toString();
				ArrayList<HashMap<String,String>> getMethodConfig = methods.get(methodName);
				if(getMethodConfig == null){
					getMethodConfig = new ArrayList<HashMap<String,String>>();
					for(@SuppressWarnings("unused") CtClass type:types){
						getMethodConfig.add(new HashMap<String,String>());
					}
					methods.put(methodName, getMethodConfig);
				}
				return getMethodConfig;
			}
		}
		
		public PackageMerger getHotswapPackageMerger(String resetMethod){
			PackageMerger pm = this.hotswaps.get(resetMethod);
			if(pm == null){
				pm = new PackageMerger();
				this.hotswaps.put(resetMethod, pm);
			}
			return pm;
		}
		
		public ControlPanel getControlPanel(String name){
			ControlPanel getControlPanel = controlPanels.get(name);
			if(getControlPanel == null){
				getControlPanel = new ControlPanel();
				controlPanels.put(name, getControlPanel);
			}
			return getControlPanel;
		}
	}
	
	private static class WrappedClass{
		public final CtClass innerClass;
		public final Object innerMember;
		public WrappedClass(CtClass innerClass,Object innerMember){
			this.innerClass = innerClass;
			this.innerMember = innerMember;
		}
	}
}
